/*********************************************************************************/
/*                                                                               */
/*      SPI Driver                                                               */
/*                                                                               */
/*      Last updated by:   CBS                                                   */
/*      Last update date:  13/01/15                                              */
/*      Revision:          0                                                     */
/*      Copyright:         DENSO                                                 */
/*                                                                               */
/*********************************************************************************/
/*********************************************************************************/
/*  include files                                                                */
/*********************************************************************************/
#include "Spi.h"
#include "Spi_Cfg.h"
#include "MPC5674F_MVx264.h"
#include "Mcu.h"
#include "Std_Types.h"
#include "Platform_Types.h"
#include "Dma.h"
#include <assert.h>

/*********************************************************************************/
/*  defines / data types / structs / unions / macros                             */
/*********************************************************************************/
/* Templates for Rx/Tx DMA structures */
struct tcd_t Spi_DmaTx =
{
    0, /* SADDR */
    0, /* SMOD */
    DMA_TRANSFER_SIZE_16BITS, /* SSIZE */
    0, /* DMOD */
    DMA_TRANSFER_SIZE_16BITS, /* DSIZE */
    4, /* SOFF */
    4, /* NBYTES */
    0, /* SLAST */
    0, /* DADDR */
    0, /* CITERE_LINK */
    0, /* CITER */
    0, /* DOFF */
    0, /* DLAST_SGA */
    0, /* BITERE_LINK */
    0, /* BITER */
    0, /* BWC */
    0, /* MAJORLINKCH */
    0, /* DONE */
    0, /* ACTIVE */
    0, /* MAJORE_LINK */
    0, /* E_SG */
    0, /* D_REQ */
    0, /* INT_HALF */
    0, /* INT_MAJ */
    0  /* START */
};

struct tcd_t Spi_DmaRx =
{
    0, /* SADDR */
    0, /* SMOD */
    DMA_TRANSFER_SIZE_16BITS, /* SSIZE */
    0, /* DMOD */
    DMA_TRANSFER_SIZE_16BITS, /* DSIZE */
    0, /* SOFF */
    4, /* NBYTES */
    0, /* SLAST */
    0, /* DADDR */
    0, /* CITERE_LINK */
    1, /* CITER */
    4, /* DOFF */
    0, /* DLAST_SGA */
    0, /* BITERE_LINK */
    1, /* BITER */
    0, /* BWC */
    0, /* MAJORLINKCH */
    0, /* DONE */
    0, /* ACTIVE */
    0, /* MAJORE_LINK */
    0, /* E_SG */
    0, /* D_REQ */
    0, /* INT_HALF */
#if defined(__DMA_INT)
    1, /* INT_MAJ */
#else
    0,
#endif 
    0  /* START */
};
#define GET_HW(_channel)    ( struct DSPI_tag *)((U4)&DSPI_A + 0x4000 * (_channel))

typedef struct{
    const Spi_DataType *src; /* pointer to source buffer */
    
    Spi_DataType *dest; /* pointer to destination buffer */
    
    Spi_NumberOfDataType length; /* number of elements of Spi_DataType in dest buffer */
    
    Bool active; /* set if the buffer is configured */
    
} Spi_EbType;

Spi_EbType Spi_Eb[SPI_MAX_CHANNEL];

typedef struct{
    U1 ctarId; 
} Spi_ChannelInfoType;

Spi_ChannelInfoType Spi_ChannelInfo[SPI_MAX_CHANNEL];


/* E2 read = cmd + addr + data = 1 + 2 + 64 ) = 67 ~ 72 */
#define SPI_INTERNAL_MTU 72 

typedef struct{
    Dma_ChannelType dmaTxChannel;
    struct tcd_t dmaTxTCD;
    
    Dma_ChannelType dmaRxChannel;
    struct tcd_t dmaRxTCD;
    
    Spi_CommandType txQueue[SPI_INTERNAL_MTU];
    
    U4 rxQueue[SPI_INTERNAL_MTU];
    
    U4 txCurrIndex;
    
    U4 channelCodes[7];
    
    Spi_StatusType status;
    
    const Spi_JobConfigType *currJob;
    const U4 *currJobIndexPtr;
    const Spi_SequenceConfigType *currSeqPtr;
    
} Spi_UnitType;

typedef struct{
    Spi_SeqResultType seqResult;
} Spi_SeqUnitType;

typedef struct{
    Spi_JobResultType jobResult;
} Spi_JobUnitType;

typedef struct{
    
    Bool initRun;
    
    const Spi_ConfigType *configPtr;
    
    Spi_EbType *exBufPtr;
    
    Spi_ChannelInfoType *channelInfo;
    
    U4 spiHwConfigured;
    
    U4 totalNbrOfTransfers;
    U4 totalNbrOfStartedJobs;
    U4 totalNbrOfWaitTXRXS;
    U4 totalNbrOfWaitRxDMA;

} Spi_GlobalType;


/* instance */
Spi_GlobalType Spi_Global ;

Spi_UnitType  Spi_Unit[4];
Spi_SeqUnitType Spi_SeqUnit[SPI_MAX_SEQUENCE];
Spi_JobUnitType Spi_JobUnit[SPI_MAX_JOB];

U4 clk_table_asc[]    =  {2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536 };
U4 clk_table_cssck[]  =  {2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536 };
U2 clk_table_br[]     =  {2,4,6,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768 };
U1 clk_table_pasc[]   =  { 1,3,5,7 };
U1 clk_table_pcssck[] =  { 1,3,5,7 };
U1 clk_table_pbr[]    =  { 2,3,5,7 };

/*********************************************************************************/
/*  Prototypes                                                                   */
/*********************************************************************************/
static void Spi_SetupCTAR( Spi_HWUnitType unit,const Spi_ExternalDeviceType *extDev,
                           Spi_ChannelType ctar_unit,U1 width );
void Spi_JobWrite( Spi_JobType jobIndex );
void Spi_SetSlave(U4 unit);
void Spi_InitController( U4 unit );
static void Spi_DmaSetup( U4 unit );
static void Spi_JobWrite( Spi_JobType jobIndex );
static void Spi_SeqWrite( Spi_SequenceType seqIndex);         
static Bool Spi_AnyPendingJobs( Spi_SequenceType Sequence );
static void Spi_SetJobResult( Spi_JobType Job, Spi_JobResultType result );
static void Spi_SetHWUnitStatus( Spi_HWUnitType HWUnit, Spi_StatusType status );
static const Spi_ExternalDeviceType *Spi_GetExternalDevicePtrFromIndex( Spi_ExternalDeviceTypeType deviceType);
static const Spi_JobConfigType *Spi_GetJobPtrFromIndex( Spi_JobType jobIndex );
static const Spi_SequenceConfigType *Spi_GetSeqPtrFromIndex( Spi_SequenceType SeqIndex );
static Spi_UnitType *Spi_GetUnitPtrFromIndex( U4 unit );
static Bool Spi_ShareJobs(Spi_SequenceType seq1, Spi_SequenceType seq2 );
static void Spi_SetSequenceResult( Spi_SequenceType Sequence, Spi_SeqResultType result );
static U4 Spi_GetNextJob( Spi_UnitType *spiUnit );
static U4 Spi_WriteNextJob( Spi_UnitType *spiUnit );

/*********************************************************************************/
/*  API Functions                                                                */
/*********************************************************************************/
/*********************************************************************************/
/*  Service name    :   Spi_Init                                                 */
/*  Sync/Async      :   Synchronous                                              */
/*  Reentrancy      :   Non Reentrant                                            */
/*  Parameters (in) :   Pointer to SPI driver configuration.                     */
/*  Return value    :   None                                                     */
/*  Discription     :   This service initializes the SPI driver.                 */
/*********************************************************************************/
void Spi_Init( const Spi_ConfigType *ConfigPtr ) {
    
    U4 i;
    U4 j;
    const Spi_JobConfigType *jobConfig2;
    Spi_Global.configPtr = ConfigPtr;
    Spi_Global.exBufPtr = Spi_Eb;
    
    
  	/* PIN configuration */
	for(i=93;i<110;i++)
	{
		SIU.PCR[i].B.PA = 0x01;
	}
    SIU.PCR[93].B.IBE = 0;
    SIU.PCR[93].B.OBE = 1;
    SIU.PCR[94].B.IBE = 1;
    SIU.PCR[94].B.OBE = 0;
    SIU.PCR[95].B.IBE = 0;
    SIU.PCR[95].B.OBE = 1;/* manual page 226*/
    SIU.PCR[96].B.IBE = 0;
    SIU.PCR[96].B.OBE = 1;
    SIU.PCR[102].B.IBE = 1;
    SIU.PCR[102].B.OBE = 0;
    SIU.PCR[103].B.IBE = 1;
    SIU.PCR[103].B.OBE = 0;
    SIU.PCR[104].B.IBE = 0;
    SIU.PCR[104].B.OBE = 1;
    SIU.PCR[105].B.IBE = 1;
    SIU.PCR[105].B.OBE = 0;

    /* Set all sequence results to OK */
    for(i = 0;i<SPI_MAX_SEQUENCE;i++) {
        Spi_SetSequenceResult(i,SPI_SEQ_OK);
    }

    /* Figure out what HW controllers are used */
    for(j = 0;j<Spi_GetJobCnt();j++){
        jobConfig2 = &Spi_Global.configPtr->SpiJobConfig[j];
        Spi_Global.spiHwConfigured |= (1<<jobConfig2->SpiHwUnit);
    }
    
    /* Initialize controllers used */   
    {
            Spi_InitController(0);
            Spi_SetHWUnitStatus(0,SPI_IDLE);


            Spi_Unit[0].dmaTxChannel = ConfigPtr->SpiHwConfig[0].TxDmaChannel;
            Spi_Unit[0].dmaRxChannel = ConfigPtr->SpiHwConfig[0].RxDmaChannel;
            Spi_DmaSetup(0);
            
            Spi_InitController(1);
            Spi_SetHWUnitStatus(1,SPI_IDLE);


            Spi_Unit[1].dmaTxChannel = ConfigPtr->SpiHwConfig[1].TxDmaChannel;
            Spi_Unit[1].dmaRxChannel = ConfigPtr->SpiHwConfig[1].RxDmaChannel;
            Spi_DmaSetup(1);

    }

    /* Setup CTARS, configuration */
    {
        Spi_UnitType *spiUnit;

        U4 j = 0;
        U1 k;
        U4 l;
        U4 channelCode;
        U4 channelIndex;

        const Spi_JobConfigType *jobConfig;
        const Spi_ChannelConfigType *chConfig;

        for(j=0;j < Spi_GetJobCnt();j++){
            jobConfig = &Spi_Global.configPtr->SpiJobConfig[j];
            spiUnit = GET_SPI_UNIT_PTR( jobConfig->SpiHwUnit );

            /* Also find the controllers used while we are at it */
            Spi_Global.spiHwConfigured |= (1<<jobConfig->SpiHwUnit);

            /* set the job status */
            Spi_SetJobResult(j,SPI_JOB_OK);

            l=0;
                /* Go through all the jobs and it's channels to setup CTAS */
                /* A job have the same physical controller ( SpiHwUnit ) */
            while( (channelIndex = jobConfig->ChannelAssignment[l++]) != NOT_VALID) {
                chConfig = &Spi_Global.configPtr->SpiChannelConfig[channelIndex];

                /* Form a channel code from <external_device_id><channel width> */
                channelCode = ((jobConfig->DeviceAssignment<<8) +  chConfig->SpiDataWidth);
                for(k=0;k<7;k++) {
                    if( spiUnit->channelCodes[k] == channelCode ) {
                        Spi_ChannelInfo[channelIndex].ctarId = k;
                        break;
                    }

                    if( spiUnit->channelCodes[k] == NOT_VALID) {
                        /* Add to list */
                        spiUnit->channelCodes[k] = channelCode;
                        /* Assign the CTAR index to channel info */
                        Spi_SetupCTAR(  jobConfig->SpiHwUnit,
                                Spi_GetExternalDevicePtrFromIndex( jobConfig->DeviceAssignment ),
                                k,
                                chConfig->SpiDataWidth );

                        Spi_ChannelInfo[channelIndex].ctarId = k;
                        break;
                    }/* if */
                }/* for */
            }/* while */
        }/* for */
    }

    Spi_Global.initRun = TRUE;        
}

/*********************************************************************************/
/*  Service name    :   Spi_DeInit                                               */
/*  Sync/Async      :   Synchronous                                              */
/*  Reentrancy      :   Non Reentrant                                            */
/*  Parameters (in) :   None                                                     */
/*  Return value    :   E_OK:de-initialisation command has been accepted         */
/*                      E_NOT_OK:de-initialisation command has not been accepted */
/*  Discription     :   This service de-initializes the SPI driver.              */
/*********************************************************************************/
Std_ReturnType Spi_DeInit( void ){
    
    VALIDATE_W_RV( ( TRUE == Spi_Global.initRun ), SPI_DEINIT_SERVICE_ID, SPI_E_UNINIT, E_NOT_OK );
    
    if( Spi_GetStatus() == SPI_BUSY ){
        return E_NOT_OK;
    }
    
    DSPI_A.MCR.B.MDIS = 1;
    DSPI_B.MCR.B.MDIS = 1;
    
    Spi_SetHWUnitStatus(0,SPI_IDLE);
    Spi_SetHWUnitStatus(1,SPI_IDLE);
        
    Spi_Global.configPtr = NULL;
    Spi_Global.initRun = FALSE;

    return E_OK;
}

/*********************************************************************************/
/*  Service name    :   Spi_WriteIB                                              */
/*  Sync/Async      :   Synchronous                                              */
/*  Reentrancy      :   Reentrant                                                */
/*  Parameters (in) :   Channel ID                                               */
/*                      Pointer to source data buffer                            */
/*  Return value    :   E_OK: write command has been accepted                    */
/*                      E_NOT_OK: write command has not been accepted            */
/*  Discription     :   This service writes one or more data to an IB SPI        */
/*                      Handler/Driver Channel specified by parameter.           */
/*********************************************************************************/
Std_ReturnType Spi_WriteIB( Spi_ChannelType Channel, const Spi_DataType *DataBufferPtr ){
    Std_ReturnType rv;
    Spi_CommandType cmd;
    U1 number; 
    U1 iterator; 

    number = 4;
    rv = E_NOT_OK;
    iterator = 0;
    
    
    if(DataBufferPtr != NULL)
    {
        DSPI_A.MCR.B.CLR_TXF = 1;
        DSPI_A.MCR.B.CLR_RXF = 1;

    	while(iterator < number - 1)
    	{
    	    cmd.B.CONT = 1;
    	    cmd.B.PCS0 = 1;
    	    cmd.B.EOQ  = 0;
    	    cmd.B.CTAS = 0;
    	    cmd.B.TXDATA = DataBufferPtr[iterator];
    	    DSPI_A.PUSHR.R = cmd.R;
    	    iterator++;
    	}
    	cmd.B.CONT = 0;
    	cmd.B.PCS0 = 1; 
    	cmd.B.EOQ  = 1;
	  	cmd.B.CTAS = 0;

    	cmd.B.TXDATA = DataBufferPtr[iterator];
    	DSPI_A.PUSHR.R = cmd.R;
     
        rv = E_OK;
  	}
    else
    {
        iterator = 0;
    	while(iterator < number - 1)
    	{
    	    cmd.B.CONT = 1;
        	cmd.B.PCS0 = 1;
        	cmd.B.EOQ  = 0;
        	cmd.B.CTAS = 0;
        	cmd.B.TXDATA = Spi_Global.configPtr->SpiChannelConfig[iterator].SpiDefaultData;
        	DSPI_A.PUSHR.R = cmd.R;
        	iterator++;
    	}
    	cmd.B.CONT = 0;
    	cmd.B.PCS0 = 1;
    	cmd.B.EOQ  = 1;
    	cmd.B.CTAS = 0;
    	cmd.B.TXDATA = Spi_Global.configPtr->SpiChannelConfig[iterator].SpiDefaultData;
    	DSPI_A.PUSHR.R = cmd.R; 
    
        rv = E_NOT_OK;
    }

    return rv;
}

/*********************************************************************************/
/*  Service name    :   Spi_ReadIB                                               */
/*  Sync/Async      :   Synchronous                                              */
/*  Reentrancy      :   Reentrant                                                */
/*  Parameters (in) :   Channel ID                                               */
/*  Return value    :   E_OK: read command has been accepted                     */
/*                      E_NOT_OK: read command has not been accepted             */
/*  Discription     :   This service reads synchronously one or more data from   */
/*                      an IB SPI Handler/Driver Channel specified by parameter. */
/*********************************************************************************/
Std_ReturnType Spi_ReadIB( Spi_ChannelType Channel, Spi_DataType* DataBufferPointer ) {
    Std_ReturnType rv;
    U1 iterator; 

    rv = E_NOT_OK;
    iterator = 0;
    
    if(DataBufferPointer != NULL)
    {
        while(iterator < 4)
        {
   	    	DataBufferPointer[iterator] = DSPI_A.POPR.R; 
   	    	iterator++;
        }
        rv = E_OK;
    }
    
    return rv;
}

/*********************************************************************************/
/*  Service name    :   Spi_GetStatus                                            */
/*  Sync/Async      :   Synchronous                                              */
/*  Reentrancy      :   Reentrant                                                */
/*  Parameters (in) :   None                                                     */
/*  Return value    :   Module status                                            */
/*  Discription     :   This service returns the SPI Handler/Driver software     */
/*                      module status.                                           */      
/*********************************************************************************/
Spi_StatusType Spi_GetStatus( void ){
    /* Check all sequences if they have any job pending */
    U4 i;
    for( i=0; i < SPI_MAX_SEQUENCE;i++) {
        if( Spi_GetSequenceResult(i) == SPI_SEQ_PENDING ) {
            return SPI_BUSY;
        }
    }
    if( !Spi_Global.initRun ) {
        return SPI_UNINIT;
    }

    return SPI_IDLE;
}

/*********************************************************************************/
/*  Service name    :   Spi_SyncTransmit                                         */
/*  Sync/Async      :   Asynchronous                                             */
/*  Reentrancy      :   Reentrant                                                */
/*  Parameters (in) :   Sequence ID                                              */
/*  Return value    :   E_OK: transimission command has been accepted            */
/*                      E_NOT_OK: transmission command has not been accepted     */
/*  Discription     :   This service transmits data on the SPI bus               */
/*********************************************************************************/
Std_ReturnType Spi_SyncTransmit( Spi_SequenceType Sequence ) 
{
    DSPI_A.MCR.B.HALT = 0;
    DSPI_B.MCR.B.HALT = 0;


    if( Spi_GetSequenceResult(Sequence) == SPI_SEQ_PENDING  ) {
    return E_NOT_OK;        /* SPI157 */
    }
    
    if( Spi_AnyPendingJobs(Sequence) ) {
        return E_NOT_OK;
    }


    return E_OK;
}

#if SPI_VERSION_INFO_API == STD_ON

static Std_VersionInfoType SPI_VersionInfo ={
    (U2)SPI_VENDOR_ID, 
    (U2)SPI_MODULE_ID,
    (U2)SPI_INSTANCE_ID,
    
    (U1)SPI_SW_MAJOR_VERSION,
    (U1)SPI_SW_MINOR_VERSION,
    (U1)SPI_SW_PATCH_VERSION,
    
    (U1)SPI_AR_MAJOR_VERSION,
    (U1)SPI_AR_MINOR_VERSION,
    (U1)SPI_AR_PATCH_VERSION 
};

/**********************************************************************************/
/*  Service name    :   Spi_GetVersionInfo                                        */
/*  Sync/Async      :   Synchronous                                               */
/*  Reentrancy      :   Reentrant                                                 */
/*  Parameters (in) :   versioninfo   Pointer to where to store the version       */
/*                                    information of this module.                 */
/*  Return value    :   None                                                      */
/*  Discription     :   This service returns the version information of this      */
/*                      module.                                                   */
/**********************************************************************************/
void Spi_GetVersionInfo(
    Std_VersionInfoType* versioninfo){
    
    versioninfo->vendorID = SPI_VersionInfo.vendorID;
    versioninfo->moduleID = SPI_VersionInfo.moduleID;
    versioninfo->instanceID = SPI_VersionInfo.instanceID;
    
    versioninfo->sw_major_version = SPI_VersionInfo.sw_major_version;
    versioninfo->sw_minor_version = SPI_VersionInfo.sw_minor_version;
    versioninfo->sw_patch_version = SPI_VersionInfo.sw_patch_version;
    
    versioninfo->ar_major_version = SPI_VersionInfo.ar_major_version;
    versioninfo->ar_minor_version = SPI_VersionInfo.ar_minor_version;
    versioninfo->ar_patch_version = SPI_VersionInfo.ar_patch_version;
    
}
#endif /* SPI_VERSION_INFO_API */
                  

/*********************************************************************************/
/*  Local Functions                                                              */
/*********************************************************************************/
/*********************************************************************************/
/*  Service name    :   Spi_GetJobResult                                         */
/*  Sync/Async      :   Synchronous                                              */
/*  Reentrancy      :   Reentrant                                                */
/*  Parameters (in) :   Job ID                                                   */
/*  Return value    :   Last transmission result                                 */
/*  Discription     :   This service returns the last transmission result of the */
/*                      specified Job.                                           */
/*********************************************************************************/
Spi_JobResultType Spi_GetJobResult ( Spi_JobType Job ) {
    return Spi_JobUnit[Job].jobResult;
}

/*********************************************************************************/
/*  Service name    :   Spi_GetSequenceResult                                    */
/*  Sync/Async      :   Synchronous                                              */
/*  Reentrancy      :   Reentrant                                                */
/*  Parameters (in) :   Sequence ID                                              */
/*  Return value    :   Last transmission result                                 */
/*  Discription     :   This service returns the last transmission result of the */
/*                      specified Sequence.                                      */
/*********************************************************************************/
Spi_SeqResultType Spi_GetSequenceResult(Spi_SequenceType Sequence ) {
    Spi_SeqResultType rv;

    rv = Spi_SeqUnit[Sequence].seqResult;

    return rv;
}

static void Spi_SetupCTAR( Spi_HWUnitType unit,
                           const Spi_ExternalDeviceType *extDev,
                           Spi_ChannelType ctar_unit,
                           U1 width ){
    U4 clock;
    U4 pre_br;
    U4 i;
    U4 j;
    U4 tmp;
    
    struct DSPI_tag *spiHw = GET_SPI_HW_PTR(unit);

    /*************************************************/ 
    /* BAUDRATE CALCULATION                          */
    /* -----------------------------                 */
    /* Baudrate = Fsys/ PBR * ( 1+ DBR) / BR         */
    /* PBR range: 2 to 7                             */
    /* DBR range: 0 to 1                             */
    /* BR : 2 to 32768                               */
    /*                                               */
    /* To make this easy set DBR = 0 and PBR=2       */
    /* --> BR=Fsys/(Baudrate.* 2 )                   */
    /*************************************************/
    #if 0    /* MCU */
    clock = McuE_GetPeripheralClock(Spi_Global.configPtr->SpiHwConfig[unit].PeripheralClock);
    #endif
    clock = 40000000;

    spiHw->CTAR[ctar_unit].B.DBR = 0;
    spiHw->CTAR[ctar_unit].B.PBR = 0;
    pre_br = clock/(extDev->SpiBaudrate*clk_table_pbr[spiHw->CTAR[ctar_unit].B.PBR]);

    /* find */
    for(i=0;i<sizeof(clk_table_br)/sizeof(clk_table_br[0]);i++) {
        if( clk_table_br[i] >= pre_br ) {
            break;
        }
    }

    /* set */
    spiHw->CTAR[ctar_unit].B.BR = i;

    /********************************************************************************/ 
    /* For other timings autosar only specifies SpiTimeClk2Cs == "After SCK delay"  */ 
    /* in Freescale language. The dumb thing is that this should be a relative time */
    /* to the clock. Not fixed.                                                     */
    /* Autosar specifies 0.0--100.0 ms(float)                                       */
    /* Our intepretation is 0--1000000 ns (U4)                                      */
    /*                                                                              */
    /* AFTER SCK DELAY:                                                             */
    /* -----------------------------                                                */
    /* Tasc = 1/Fsys * PASC  * ASC [s]                                              */
    /*                                                                              */
    /* Assume the Tasc get's here in ns( typical range is ~10ns )                   */
    /********************************************************************************/
    
    /* calculate the PASC * ASC value... */
    tmp = extDev->SpiTimeClk2Cs * (clock / 1000000 );
    
    {
    U4 best_i=0;
    U4 best_j=0;
    U4 b_value = INT_MAX;
    U4 tt;


    /* Find the best match of Prescaler and Scaler value */
        for(i=0;i<ARRAY_SIZE(clk_table_pasc);i++) {
            for(j=0;j<ARRAY_SIZE(clk_table_asc);j++) {
                tt = ((U4)clk_table_pasc[i]*clk_table_asc[j]*1000 - tmp)> 0? 
                     ((U4)clk_table_pasc[i]*clk_table_asc[j]*1000 - tmp):
                     0 - ((U4)clk_table_pasc[i]*clk_table_asc[j]*1000 - tmp);
                if( tt < b_value ) {
                    best_i = i;
                    best_j = j;
                    b_value = tt;
                    }
                }
            }

    /* After SCK delay. */
    spiHw->CTAR[ctar_unit].B.PASC = best_i;
    spiHw->CTAR[ctar_unit].B.ASC = best_j;
    }    
     
  /* The PCS to SCK delay is the delay between the assertion of PCS and
   * the first edge the SCK.
   *
   * PCS TO SCK DELAY:
   * -----------------------------
   * Tcsc = 1/Fsys * PCSSCK * CSSCK [s]
   */

  /* Calc the PCSSCK * CSSCK value */
    tmp = extDev->SpiTimeCs2Clk * (clock / 1000000 );

    {
    U4 best_i=0;
    U4 best_j=0;
    U4 b_value = INT_MAX;
    U4 tt;

    /* Find the best match of Prescaler and Scaler value */
    for(i=0;i<ARRAY_SIZE(clk_table_pcssck);i++) {
        for(j=0;j<ARRAY_SIZE(clk_table_cssck);j++) {
            tt = ((U4)clk_table_pcssck[i]*clk_table_cssck[j]*1000 - tmp)>0?
                 ((U4)clk_table_pasc[i]*clk_table_asc[j]*1000 - tmp):
                 0 - ((U4)clk_table_pasc[i]*clk_table_asc[j]*1000 - tmp);
            if( tt < b_value ){
                best_i = i;
                best_j = j;
                b_value = tt;
            }
        }
    }

    /* PCS to SCK delay */
    spiHw->CTAR[ctar_unit].B.PCSSCK = best_i;
    spiHw->CTAR[ctar_unit].B.CSSCK = best_j;
    }

    /* Time that PCS is high between transfers */
    spiHw->CTAR[ctar_unit].B.PDT = 2;
    spiHw->CTAR[ctar_unit].B.DT = 2;
  
    /* Data is transferred MSB first */
    spiHw->CTAR[ctar_unit].B.LSBFE = 0;
    
    /* Set mode */
    spiHw->CTAR[ctar_unit].B.FMSZ = width - 1;
    spiHw->CTAR[ctar_unit].B.CPHA = ( extDev->SpiDataShiftEdge == SPI_EDGE_LEADING ) ?  0 : 1;
    spiHw->CTAR[ctar_unit].B.CPOL = ( extDev->SpiCsPolarity == STD_LOW ) ?  0 : 1;

    if( extDev->SpiCsPolarity == STD_HIGH ) {
        spiHw->MCR.R &= ~(1 <<(16+extDev->SpiCsIdentifier));
    } else {
        spiHw->MCR.R |= (1 <<(16+extDev->SpiCsIdentifier));
    }

}

void Spi_SetSlave(U4 unit)
{
    struct DSPI_tag *spiHw = GET_SPI_HW_PTR(unit);
    Spi_UnitType *spiUnit = GET_SPI_UNIT_PTR(unit);
    U4 i;

    /* Module configuration register. */
    /* Slave mode. */
    spiHw->MCR.B.MSTR = 0;
    
    spiHw->MCR.B.DCONF = 0x0;
    /* No freeze. Run SPI when debugger is stopped. */
    spiHw->MCR.B.FRZ = 1;
    /* PSC5 as regular CS. */
    spiHw->MCR.B.PCSSE = 0;

    /* Enable FIFO's. */
    spiHw->MCR.B.DIS_RXF = 0;
    spiHw->MCR.B.DIS_TXF = 0;

    /* Set all active low. */
    spiHw->MCR.B.PCSIS0 = 1;
    spiHw->MCR.B.PCSIS1 = 1;
    spiHw->MCR.B.PCSIS2 = 1;
    spiHw->MCR.B.PCSIS3 = 1;
    spiHw->MCR.B.PCSIS4 = 1;
    spiHw->MCR.B.PCSIS5 = 1;

    /* DMA TX FIFO fill. */
    spiHw->RSER.B.TFFFRE = 1;
    spiHw->RSER.B.TFFFDIRS = 1;

    /* DMA RX FIFO drain. */
    spiHw->RSER.B.RFDFRE = 1;
    spiHw->RSER.B.RFDFDIRS = 1;

    /* Setup CTAR's channel codes */
    for(i=0;i<7;i++) {
        spiUnit->channelCodes[i] = NOT_VALID;
    }
    
    spiHw->MCR.B.CLR_TXF = 1;
    spiHw->MCR.B.CLR_RXF = 1;

    /* Force to stopped state. */
    spiHw->MCR.B.HALT = 1;

    spiHw->SR.B.EOQF = 1;

    /* Enable clocks. */
    spiHw->MCR.B.MDIS = 0;

    spiHw->CTAR[0].B.FMSZ = 0x0f;
    spiHw->CTAR[0].B.CPHA = 1;
    
    spiHw->MCR.B.HALT = 0;

    spiHw->PUSHR.R =  0x800100AA; 
    spiHw->PUSHR.R =  0x8001AA00; 
    spiHw->PUSHR.R =  0x80010055; 
    spiHw->PUSHR.R =  0x08015500;
    
   

}

void Spi_InitController( U4 unit ) {
    struct DSPI_tag *spiHw = GET_SPI_HW_PTR(unit);
    Spi_UnitType *spiUnit = GET_SPI_UNIT_PTR(unit);
    U4 i;
    
    /* Module configuration register. */
    /* Master mode. */
    spiHw->MCR.B.MSTR = 1;
    spiHw->MCR.B.DCONF = 0x0;
    /* No freeze. Run SPI when debugger is stopped. */
    spiHw->MCR.B.FRZ = 0;
    /* PSC5 as regular CS. */
    spiHw->MCR.B.PCSSE = 0;
    
    /* Enable FIFO's. */
    spiHw->MCR.B.DIS_RXF = 0;
    spiHw->MCR.B.DIS_TXF = 0;

    /* Set all active low. */
    spiHw->MCR.B.PCSIS0 = 1;
    spiHw->MCR.B.PCSIS1 = 1;
    spiHw->MCR.B.PCSIS2 = 1;
    spiHw->MCR.B.PCSIS3 = 1;
    spiHw->MCR.B.PCSIS4 = 1;
    spiHw->MCR.B.PCSIS5 = 1;

    /* DMA TX FIFO fill. */
    spiHw->RSER.B.TFFFRE = 1;
    spiHw->RSER.B.TFFFDIRS = 1;

    /* DMA RX FIFO drain. */
    spiHw->RSER.B.RFDFRE = 1;
    spiHw->RSER.B.RFDFDIRS = 1;

    /* Setup CTAR's channel codes */
    for(i=0;i<7;i++) {
        spiUnit->channelCodes[i] = NOT_VALID;
    }

    /* Force to stopped state. */
    spiHw->MCR.B.HALT = 1;

    spiHw->SR.B.EOQF = 1;

    /* Enable clocks. */
    spiHw->MCR.B.MDIS = 0;
    
    spiHw->MCR.B.CONT_SCKE = 1;
    
    spiHw->CTAR[0].B.FMSZ = 0x0f;

}

static void Spi_DmaSetup( U4 unit )
{
    struct tcd_t *tcd;
    
    tcd = &Spi_Unit[unit].dmaTxTCD;
    *tcd = Spi_DmaTx;
    tcd->SADDR = (U4)Spi_Unit[unit].txQueue;
    tcd->DADDR = (U4)&(GET_SPI_HW_PTR(unit)->PUSHR.R);

    Dma_StopChannel(Spi_Unit[unit].dmaTxChannel);

    /* CITER and BITER set when we send */
    tcd = &Spi_Unit[unit].dmaRxTCD;
    *tcd = Spi_DmaRx;
    tcd->SADDR = (U4)&(GET_SPI_HW_PTR(unit)->POPR.R);
    tcd->DADDR = (U4)Spi_Unit[unit].rxQueue;

    Dma_StopChannel(Spi_Unit[unit].dmaRxChannel);
}

static void Spi_JobWrite( Spi_JobType jobIndex ){
    const Spi_JobConfigType *jobConfig;
    const Spi_ChannelConfigType *chConfig;
    Spi_EbType *extChBuff;
    Spi_CommandType cmd;
    Spi_UnitType *spiUnit;
    struct DSPI_tag *spiHw;
    
    U4 channelIndex;
    U4 k;
    U4 j;
    cmd.R = 0;
    
    jobConfig = &Spi_Global.configPtr->SpiJobConfig[jobIndex];
    spiUnit = GET_SPI_UNIT_PTR( jobConfig->SpiHwUnit );
    spiHw = GET_SPI_HW_PTR(jobConfig->SpiHwUnit);
    spiUnit->txCurrIndex = 0;
    spiUnit->currJob = jobConfig;
    
    Spi_SetHWUnitStatus(jobConfig->SpiHwUnit,SPI_BUSY);
    Spi_SetJobResult(jobIndex,SPI_JOB_PENDING);

    j = 0;

    while( (channelIndex = jobConfig->ChannelAssignment[j++]) != NOT_VALID)
    {
        chConfig = &Spi_Global.configPtr->SpiChannelConfig[channelIndex];
        #if ( SPI_CHANNEL_BUFFERS_ALLOWED == 1  )
        assert( chConfig->SpiChannelType == SPI_EB );
        #endif

        extChBuff = &Spi_Global.exBufPtr[channelIndex];
        
        k=0;
        
        for(k=0;k<extChBuff->length;k++) {
            U4 csId = Spi_Global.configPtr->SpiExternalDevice[jobConfig->DeviceAssignment].SpiCsIdentifier;
            /* Build cmd */
            cmd.B.CONT = 1; /* Channels should keep CS active */
                            /* A job must assert CS continuously */
            cmd.R |= (1 <<(16 + csId)); /* Set PCS */
            cmd.B.EOQ = 0;
            cmd.B.CTAS = Spi_ChannelInfo[channelIndex].ctarId;
            if( extChBuff->src != NULL ) {
                if( chConfig->SpiDataWidth > 16 ) { /* 8? */
                    cmd.B.TXDATA = (extChBuff->src[k]<<16)+(extChBuff->src[k+1]&0xff);
                    k++;
                } else {
                    cmd.B.TXDATA = extChBuff->src[k];
                }
            } 
            else {
                cmd.B.TXDATA = chConfig->SpiDefaultData;
            }

            /* Just keep on filling the tx queue */
            spiUnit->txQueue[spiUnit->txCurrIndex++].R = cmd.R;
        }
    }
    
    /* send last part */
    cmd.B.CONT = 0;     /* CS high */
    cmd.B.EOQ = 1;      /* last in queue */
    spiUnit->txQueue[--spiUnit->txCurrIndex].R = cmd.R;

    /* Set the length of the data to send */
    spiUnit->dmaTxTCD.CITER = spiUnit->txCurrIndex + 1;
    spiUnit->dmaTxTCD.BITER = spiUnit->txCurrIndex + 1;

    (Spi_Global.totalNbrOfStartedJobs)++;

    
    {
        U4 unit = jobConfig->SpiHwUnit;

        Spi_UnitType *spiUnit = GET_SPI_UNIT_PTR(unit);
        struct DSPI_tag *spiHw = GET_SPI_HW_PTR(unit);


        Dma_ConfigureChannel ((struct tcd_t *)&spiUnit->dmaTxTCD, spiUnit->dmaTxChannel);
        Dma_ConfigureChannel ((struct tcd_t *)&spiUnit->dmaRxTCD, spiUnit->dmaRxChannel );
        /* Flush TX/Rx FIFO.  Ref. man. 23.5.1 step 8 */
        spiHw->MCR.B.CLR_RXF = 1;
        spiHw->MCR.B.CLR_TXF = 1;

        Dma_StartChannel (spiUnit->dmaRxChannel);
        Dma_StartChannel (spiUnit->dmaTxChannel);

        /* Step 9. Clear TCNT */
        spiHw->TCR.B.TCNT = 0;

        /* This will trig a new transfer. Ref. man. 23.5.1 step 11 */
        spiHw->SR.B.EOQF = 1;
        spiHw->MCR.B.HALT = 0;

        /* Since it's not obvious on how to tell when a SPI sequence
           is sent, keep things below to what things have been tested.*/
        #if 0
        /* Wait for transfer to complete. */
        while (!spiHw->SR.B.EOQF) { arrggg++; }
        while (spiHw->SR.B.TXRXS) {   arrggg2++;}
        while( EDMA.TCD[spiUnit->dmaRxChannel].ACTIVE ) {;}

        #endif

    }   
}

void Spi_SeqWrite( Spi_SequenceType seqIndex)
{
    const Spi_SequenceConfigType *seqConfig;
    const Spi_JobConfigType *jobConfig;
    Spi_UnitType *spiUnit;
    Spi_JobType jobIndex;


    seqConfig = Spi_GetSeqPtrFromIndex(seqIndex);

    spiUnit = Spi_GetUnitPtrFromIndex(jobConfig->SpiHwUnit);
    /* Fill in the required fields for job and sequence */
    spiUnit->currJobIndexPtr = &seqConfig->JobAssignment[0];
    spiUnit->currSeqPtr = seqConfig;

    Spi_SetSequenceResult(seqIndex, SPI_SEQ_PENDING);

    Spi_JobWrite(jobIndex);
}

static Bool Spi_AnyPendingJobs( Spi_SequenceType Sequence ){
    U4 i ;
    for( i=0;i < SPI_MAX_SEQUENCE;i++) {
        if( i == Sequence ) {
            continue;
        }
        if( Spi_GetSequenceResult(i) == SPI_SEQ_PENDING ){
            if( Spi_ShareJobs(Sequence,i) ){
                return TRUE;
            }
        }
    }
    return FALSE;
}

Std_ReturnType Spi_SetupEB( Spi_ChannelType Channel,
                            const Spi_DataType*  SrcDataBufferPtr,
                            Spi_DataType*   DesDataBufferPtr,
                            Spi_NumberOfDataType Length )
{
    Spi_EbType *extChBuff= &Spi_Global.exBufPtr[Channel];

    const Spi_ChannelConfigType *chConfig = &Spi_Global.configPtr->SpiChannelConfig[Channel];

    if( chConfig->SpiChannelType == SPI_EB ) {
        extChBuff->src = SrcDataBufferPtr;
        extChBuff->dest = DesDataBufferPtr;
        extChBuff->length = Length;
        extChBuff->active = TRUE;
    } else {
        /* NOT SUPPORTED */
        assert(0);
    }

    return E_OK;
}



static void Spi_SetJobResult( Spi_JobType Job, Spi_JobResultType result ){
    Spi_JobUnit[Job].jobResult = result;
}

static void Spi_SetHWUnitStatus( Spi_HWUnitType HWUnit, Spi_StatusType status ){
    Spi_Unit[HWUnit].status = status;
}


/* Get external ptr to device from index*/
static const Spi_ExternalDeviceType *Spi_GetExternalDevicePtrFromIndex( Spi_ExternalDeviceTypeType deviceType){
    return (&(Spi_Global.configPtr->SpiExternalDevice[(deviceType)]));
}

/* Get configuration job ptr from job index */
static const Spi_JobConfigType *Spi_GetJobPtrFromIndex( Spi_JobType jobIndex ) {
    return &Spi_Global.configPtr->SpiJobConfig[jobIndex];
}

/* Get sequence ptr from sequence index */
static const Spi_SequenceConfigType *Spi_GetSeqPtrFromIndex( Spi_SequenceType SeqIndex ) {
    return &Spi_Global.configPtr->SpiSequenceConfig[SeqIndex];
}

/* Get unit ptr from unit index */
static Spi_UnitType *Spi_GetUnitPtrFromIndex( U4 unit ) {
    return &Spi_Unit[unit];
}

/* Function to see if two sequences share jobs */
static Bool Spi_ShareJobs(Spi_SequenceType seq1, Spi_SequenceType seq2 ) {
    const U4 *jobPtr;
    const Spi_SequenceConfigType *seqConfig; 
    U4 seqMask1 = 0;
    U4 seqMask2 = 0;

    /* Search for jobs in sequence 1 */
    seqConfig = Spi_GetSeqPtrFromIndex(seq1);
    jobPtr = &seqConfig->JobAssignment[0];

    while( *jobPtr != NOT_VALID ){
        seqMask1 |= (1<<*jobPtr);
        jobPtr++;
    }

    /* Search for jobs in sequence 2 */
    seqConfig = Spi_GetSeqPtrFromIndex(seq2);
    jobPtr = &seqConfig->JobAssignment[0];

    while( *jobPtr != NOT_VALID ) {
        seqMask2 |= (1<<*jobPtr);
        jobPtr++;
    }
    if((seqMask1 & seqMask2) == 0)
    {
        return FALSE;   
    }
    else
    {
        return TRUE;
    }
}

/* Set a result for a sequence */
static void Spi_SetSequenceResult( Spi_SequenceType Sequence, Spi_SeqResultType result )
{
    Spi_SeqUnit[Sequence].seqResult = result;
}

/* Get the next job to do */
static U4 Spi_GetNextJob( Spi_UnitType *spiUnit ) 
{
    (spiUnit->currJobIndexPtr)++;
    return *(spiUnit->currJobIndexPtr);
}

/* Schedule next job to do */
static U4 Spi_WriteNextJob( Spi_UnitType *spiUnit ) {
    U4 nextJob;

    nextJob = Spi_GetNextJob(spiUnit);
    if( nextJob == NOT_VALID) {
        return NOT_VALID;
    } 
    else 
    {
        /* Schedule next job */
        Spi_JobWrite(nextJob);
    }
    return 0;
}